using Sandia.MVCFramework.Common;
using Sandia.MVCFramework.Controller;
using Sandia.MVCFramework.DataAccessor;
using Sandia.MVCFramework.DataAnalyzer;
using Sandia.MVCFramework.DataStore;
using Sandia.MVCFramework.ViewManager;
using System;
using System.Collections.Generic;
using System.Text;
using Sandia.MVCFramework.ExportTools;

namespace Sandia.MVCFramework.Plugin
{
    /// <summary>
    /// A standard implementation of IPluginLoader.
    /// </summary>
    public class StandardPluginLoader : IPluginLoader
    {
        /// <summary>
        /// The source to obtain the plugins.
        /// </summary>
        private IPluginSource pluginSource;

        /// <summary>
        /// Constructor for StandardPluginLoader. Uses the given plugin source
        /// for loading plugins.
        /// </summary>
        /// <param name="pluginSource">The source to obtain the plugins.</param>
        public StandardPluginLoader(
            IPluginSource pluginSource)
        {
            this.pluginSource = pluginSource;
        }

        #region IPluginLoader Members

        /// <summary>
        /// Load the available plugins into the given controller.
        /// </summary>
        /// <param name="controller">The controller to load the plugins into.</param>
        public void LoadPlugins(
            IController controller)
        {
            foreach (IPlugin plugin in this.pluginSource.AvailablePlugins)
            {
                this.LoadPlugin(plugin, controller);
            }
        }

        /// <summary>
        /// Load the given plugin into the given controller.
        /// </summary>
        /// <param name="plugin">The plugin to load.</param>
        /// <param name="controller">The controller to load the plugin into.</param>
        public void LoadPlugin(
            IPlugin plugin, 
            IController controller)
        {
            plugin.Init(controller, controller);

            // Tell the controller about each DataAccessor known by 
            // this plugin
            //
            foreach (IDataAccessor dataAccessor in plugin.DataAccessors)
            {
                controller.AddDataAccessor(dataAccessor);
            }

            // Tell the controller about each DataAnalyzer known by
            // this plugin
            //
            foreach (IDataAnalyzer dataAnalyzer in plugin.DataAnalyzers)
            {
                controller.AddDataAnalyzer(dataAnalyzer);
            }

            // Tell the controller about each DataStore known by
            // this plugin
            //
            foreach (IDataStore dataStore in plugin.DataStores)
            {
                controller.AddDataStore(dataStore);
            }

            // Use the collection of all the desired requests to create
            // routings.
            //
            foreach (Type requestType in plugin.RequestRoutings.Keys)
            {
                foreach (IRequestProcessor requestProcessor in
                    plugin.RequestRoutings[requestType])
                {
                    // Have the controller add a routing for this
                    // desired request; a connection between the
                    // request type and the request processor
                    //
                    controller.AddRequestRouting(
                        requestType,
                        requestProcessor);
                }
            }

            // Use the collection of all of the desire subscriptions to
            // create a kind of 'routing' similar to requests, but for
            // output rather than input.
            //
            foreach (Type subscriptionType in plugin.Subscriptions.Keys)
            {
                foreach (ISubscriber subscriber in
                    plugin.Subscriptions[subscriptionType])
                {
                    // Have the controller add a 'subscription' for this
                    // type; a connection between the type and an object
                    // that wants it when it is produced.
                    //
                    controller.Subscribe(subscriber, subscriptionType);
                }
            }

            // Tell the controller about each ViewManager known by
            // this plugin
            //
            foreach (IViewManager viewManager in plugin.ViewManagers)
            {
                controller.AddViewManager(viewManager);
            }

            // Tell the controller about each Exporter known by
            // this plugin
            //
            foreach (IExporter exporter in plugin.Exporters)
            {
                controller.AddExporter(exporter);
            }

            // Tell the controller about each Importer known by
            // this plugin
            //
            foreach (IImporter importer in plugin.Importers)
            {
                controller.AddImporter(importer);
            }
        }

        /// <summary>
        /// Removes all plugins from the given controller.
        /// </summary>
        /// <param name="controller">The controller to remove the plugin from.</param>
        public void RemovePlugins(
            IController controller)
        {
            foreach (IPlugin plugin in this.pluginSource.AvailablePlugins)
            {
                this.RemovePlugin(plugin, controller);
            }
        }

        /// <summary>
        /// Removes the given plugin from the given controller.
        /// </summary>
        /// <param name="plugin">The plugin to remove.</param>
        /// <param name="controller">The controller to remove.</param>
        public void RemovePlugin(
            IPlugin plugin, 
            IController controller)
        {
            // Remove the plugin
            foreach (IDataAccessor dataAccessor in plugin.DataAccessors)
            {
                controller.RemoveDataAccessor(dataAccessor);
            }
            foreach (IDataAnalyzer dataAnalyzer in plugin.DataAnalyzers)
            {
                controller.RemoveDataAnalyzer(dataAnalyzer);
            }
            foreach (IDataStore dataStore in plugin.DataStores)
            {
                controller.RemoveDataStore(dataStore);
            }
            foreach (Type requestType in plugin.RequestRoutings.Keys)
            {
                foreach (IRequestProcessor requestProcessor in
                    plugin.RequestRoutings[requestType])
                {
                    controller.RemoveRequestRouting(
                        requestType,
                        requestProcessor);
                }
            }
            foreach (Type subscriptionType in plugin.Subscriptions.Keys)
            {
                foreach (ISubscriber subscriber in
                    plugin.Subscriptions[subscriptionType])
                {
                    controller.RemoveSubscription(subscriber, subscriptionType);
                }
            }
            foreach (IViewManager viewManager in plugin.ViewManagers)
            {
                controller.RemoveViewManager(viewManager);
            }
            foreach (IExporter exporter in plugin.Exporters)
            {
                controller.RemoveExporter(exporter);
            }
            foreach (IImporter importer in plugin.Importers)
            {
                controller.RemoveImporter(importer);
            }
        }

        #endregion
    }
}
